/*
 * @(#)MimirRegistry.java	1.0 Apr 17, 2008
 *
 *	The MIT License
 *
 *	Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com>
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *	THE SOFTWARE.
 */
package org.eoti.db.mimir;

import org.eoti.spec.mimirdb.v1.DBConfig;
import org.eoti.spec.mimirdb.v1.DBMapping;

import java.io.File;
import java.net.URL;
import java.net.URISyntaxException;

/**
 * This class maintains the registration of the namespaces/schemas/databases
 */
public class MimirRegistry
{
	protected static String configFileName = "mimir.xml";
	protected static String configSchema = "/org/eoti/spec/mimirdb/mimirdb-1.0.xsd";

	protected DBConfig config;
	protected MimirContext<DBConfig,String> context;
	protected MimirPrefixMapper mapper;
	protected DBMapping mimirMapping;
	protected File databaseRoot;
	protected File configFile;

	/**
	 * Register Mimir on the local directory
	 * @throws MimirException if there are any problems
	 */
	public MimirRegistry()
			throws MimirException
	{
		this(new File("."));
	}

	/**
	 * Register Mimir with the specified database directory
	 *
	 * @param databaseRoot where mimir.xml can be found/created
	 * @throws MimirException if there are any problems
	 */
	public MimirRegistry(File databaseRoot)
			throws MimirException
	{
		this.databaseRoot = databaseRoot.getAbsoluteFile();
		getConfig();
	}

	public File getDatabaseRoot(){return databaseRoot;}

	/**
	 * Load Registry configuration
	 * @return dbconfig
	 * @throws MimirException if there are any problems
	 */
	public DBConfig getConfig()
			throws MimirException
	{
		if(config != null) return config;

		config = new DBConfig();
		addDefaultMappings();
		databaseRoot.mkdirs();
		configFile = new File(databaseRoot, configFileName);
		MimirContext<DBConfig,String> ctx = getContext();
		ctx.clear();
		DBConfig newConfig = ctx.read(configFile);
		if(newConfig != null)
		{
			config = newConfig;
			return config;
		}

		/**
		 * initialize
		 */
		ctx.write(configFile, config, mimirMapping);
		return config;
	}

	public MimirPrefixMapper getPrefixMapper()
	{
		if(mapper == null)
			mapper = new MimirPrefixMapper(this);

		return mapper;
	}

	public MimirContext<DBConfig,String> getContext()
	{
		if(context == null)
			context = new MimirContext<DBConfig,String>(this);

		return context;
	}

	protected void addDefaultMappings()
			throws MimirException
	{
		DBMapping xsdMapping = new DBMapping();
		xsdMapping.setNamespacePrefix("xsd");
		xsdMapping.setNamespaceURI("http://www.w3.org/2001/XMLSchema");
		addMapping(xsdMapping);

		DBMapping xsiMapping = new DBMapping();
		xsiMapping.setNamespacePrefix("xsi");
		xsiMapping.setNamespaceURI("http://www.w3.org/2001/XMLSchema-instance");
		addMapping(xsiMapping);

		DBMapping jaxbMapping = new DBMapping();
		jaxbMapping.setNamespacePrefix("jaxb");
		jaxbMapping.setNamespaceURI("http://java.sun.com/xml/ns/jaxb");
		addMapping(jaxbMapping);

		mimirMapping = new DBMapping();
		mimirMapping.setId("mimirdb-1.0");
		mimirMapping.setNamespacePrefix("mimirdb");
		mimirMapping.setNamespaceURI("http://eoti.org/spec/mimirdb/1.0");
		mimirMapping.setSpecPackage("org.eoti.spec.mimirdb.v1");
		mimirMapping.setSchema(getSchemaLocation(configSchema));
		addMapping(mimirMapping);
	}

	public void displayMappings()
	throws MimirException
	{
		for(DBMapping mapping : getConfig().getMappings())
			System.out.format("%s\n", mappingToString(mapping));
	}

	public void addMapping(DBMapping mapping)
			throws MimirException
	{
		if(containsMapping(mapping))
			removeMapping(mapping);

		getConfig().getMappings().add(mapping);
		getContext().clear();
	}

	public boolean containsMapping(DBMapping mapping)
	throws MimirException
	{
//		System.out.format("containsMapping('%s')\n", mapping.getId());
		if(mapping.getId() == null) return false;

		// DBMapping is not comparable
		for(int i=0; i<getConfig().getMappings().size(); i++)
		{
			DBMapping iMapping = getConfig().getMappings().get(i);
//			System.out.format("\t%s\n", mappingToString(iMapping));
			String id = iMapping.getId();
			if(id == null) continue;
			if(id.equals(mapping.getId())) return true;
		}
		return false;
	}

	public boolean removeMapping(DBMapping mapping)
	throws MimirException
	{
//		System.out.format("removeMapping('%s')\n", mapping.getId());
		if(mapping.getId() == null) return false;

		// DBMapping is not comparable
		for(int i=0; i<getConfig().getMappings().size(); i++)
		{
			DBMapping iMapping = getConfig().getMappings().get(i);
//			System.out.format("\t%s\n", mappingToString(iMapping));
			String id = iMapping.getId();
			if(id == null) continue;
			if(id.equals(mapping.getId()))
			{
				getConfig().getMappings().remove(i);
				return true;
			}
		}
		return false;
	}

	public String getSchemaLocation(String fqSchemaName)
			throws SchemeNotFoundException
	{
		URL location = this.getClass().getResource(fqSchemaName);
		if(location == null)
			throw new SchemeNotFoundException(this, fqSchemaName, null);

		try{
			return location.toURI().toString();
		} catch(URISyntaxException e){
			throw new SchemeNotFoundException(this, fqSchemaName, location.toExternalForm(), e);
		}
	}

	protected void saveConfig()
	throws MimirException
	{
		if(configFile == null) return; // ie: not finished initializing yet
		MimirContext<DBConfig,String> ctx = getContext();
		ctx.clear();
		ctx.write(configFile, getConfig(), mimirMapping);
	}

	public DBMapping createDefaultMapping(String project, String spec, String version)
			throws SchemeNotFoundException
	{
		DBMapping mapping = new DBMapping();
		mapping.setId(String.format("%s-%s", spec, version));
		mapping.setNamespacePrefix(spec);
		mapping.setNamespaceURI(String.format("http://eoti.org/spec/%s/%s/%s", project, spec, version));
		mapping.setSchema(getSchemaLocation(String.format("/org/eoti/spec/%s/%s-%s.xsd", project, spec, version)));
		mapping.setSpecPackage(String.format("org.eoti.spec.%s.%s", project, spec));
		mapping.setDirectory(String.format("%s/%s", spec, version));
		return mapping;
	}

	protected String mappingToString(DBMapping mapping)
	{
		return String.format(
			"[DBMapping: [ID: %s][Prefix: %s][Spec: %s][URI: %s][Dir: %s][XSD: %s]]",
			mapping.getId(),
			mapping.getNamespacePrefix(),
			mapping.getSpecPackage(),
			mapping.getNamespaceURI(),
			mapping.getDirectory(),
			mapping.getSchema()
		);
	}

	public DBMapping getMappingById(String id)
	throws MimirException
	{
		if(id == null) return null;
		for(DBMapping mapping : getConfig().getMappings())
		{
			if(id.equalsIgnoreCase(mapping.getId())) return mapping;
		}
		return null;
	}

	public DBMapping getMappingByURI(String namespaceURI)
	throws MimirException
	{
		if(namespaceURI == null) return null;
		for(DBMapping mapping : getConfig().getMappings())
		{
			if(namespaceURI.equalsIgnoreCase(mapping.getNamespaceURI())) return mapping;
		}
		return null;
	}

	public DBMapping getMappingByPrefix(String prefix)
	throws MimirException
	{
		if(prefix == null) return null;
		for(DBMapping mapping : getConfig().getMappings())
		{
			if(prefix.equalsIgnoreCase(mapping.getNamespacePrefix())) return mapping;
		}
		return null;
	}

	public DBMapping getMappingBySpecification(String specPackage)
	throws MimirException
	{
		if(specPackage == null) return null;
		for(DBMapping mapping : getConfig().getMappings())
		{
			if(specPackage.equalsIgnoreCase(mapping.getSpecPackage())) return mapping;
		}
		return null;
	}

	public DBMapping getMappingBySchema(File schema)
	throws MimirException
	{
		if(schema == null) return null;
		return getMappingBySchema(schema.getName());
	}

	public DBMapping getMappingBySchema(String schemaName)
	throws MimirException
	{
		if(schemaName == null) return null;
		for(DBMapping mapping : getConfig().getMappings())
		{
			if(schemaName.equalsIgnoreCase(mapping.getSchema())) return mapping;
		}
		return null;
	}
/*
	public ArrayList<MimirScheme> getAllSchemes()
	{
		ArrayList<MimirScheme> list = new ArrayList<MimirScheme>();
//		ExtensionFileFilter filter = new ExtensionFileFilter(MimirScheme.extension, false);
//		for(File file : databaseRoot.listFiles(filter))
		for(DBMapping mapping : getConfig().getMappings())
		{
			try{
				list.add(getScheme(mapping));
			} catch(SchemeNotFoundException e){
// should probably log but not abort
//				e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
			} catch(SchemeValidationException e){
// should probably log but not abort
//				e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
			}
		}
		return list;
	}
*/
//	public MimirScheme createScheme(DBMapping mapping, File xsdToCopy)
//			throws IOException, SchemeNotFoundException, SchemeValidationException, MimirException
//	{
//		MimirScheme scheme = new MimirScheme(this, mapping, xsdToCopy);
//		addMapping(mapping);
//		return scheme;
//	}
}
